package com.alan.api.util;

import com.alan.api.share.common.enums.ResponseCodeEnum;
import com.alan.api.share.dto.common.RequestBase;
import com.alan.api.share.dto.common.ResponseBase;
import com.alan.server.enums.ServiceErrorCodeEnum;
import com.alan.server.exception.ServiceException;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.xiaoleilu.hutool.crypto.digest.DigestAlgorithm;
import com.xiaoleilu.hutool.crypto.digest.Digester;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.*;

/**
 * Created by Alan on 2017/10/27.
 */
@Slf4j
public class ApiSignUtils {

    private static String apiKey = "afsfsferv198w1veg6g4a683g1g63v1s3";

    /**
     * 校验签名
     * @param request
     * @param response
     * @return
     * @throws IOException
     * @throws JsonGenerationException
     * @throws JsonMappingException
     */
    public static ResponseBase checkSign(HttpServletRequest request,
                                         HttpServletResponse response) throws IOException {
        ResponseBase responseBase = new ResponseBase();
        responseBase.setEnum(ResponseCodeEnum.SUCCESS);
        String sign = request.getParameter("sign");
        if(StringUtils.isEmpty(sign)){//签名为空
            responseBase.setEnum(ResponseCodeEnum.INPUT_MUST_NOT_NULL);
            return responseBase;
        }
        sign = sign.trim();
        String scr = apiKey;
        //TODO test code
        if(sign.equals("test")){
            return responseBase;
        }
        String check = getSignature(request,scr);
        if(!sign.equals(check)){//校验失败
            responseBase.setEnum(ResponseCodeEnum.REQUEST_VERIFY_FAIL);
            return responseBase;
        }
        return responseBase;
    }

    /**
     * 签名生成算法
     * @param request
     * @param secret    签名密钥
     * @return  签名
     */
    public static String getSignature(HttpServletRequest request,String secret) {
        Map map = new HashMap();
        Enumeration paramNames = request.getParameterNames();
        while (paramNames.hasMoreElements()) {
            String paramName = (String) paramNames.nextElement();
            if(paramName.trim().equals("sign")||paramName.trim().equals("url")){
                continue;
            }
            String[] paramValues = request.getParameterValues(paramName);
            if (paramValues.length == 1) {
                String paramValue = paramValues[0];
                try {
//                	paramValue = new String(request.getParameter(paramName).getBytes("iso-8859-1"), "utf-8");
                    paramValue = URLEncoder.encode(StringUtils.deleteWhitespace(paramValue), "UTF-8");
                    if (paramValue.length() != 0) {
                        map.put(paramName, paramValue);
//	                    System.out.println(paramName+":"+paramValue);
                    }
                } catch (UnsupportedEncodingException e) {
                    log.error("签名生成异常: {} ",e);
                }
            }
        }
        Object[] key = map.keySet().toArray();
        Arrays.sort(key);
        // 遍历排序后的字典，将所有参数按"key=value"格式拼接在一起
        StringBuilder basestring = new StringBuilder();
        for (int i = 0; i < key.length; i++) {
            basestring.append(key[i]).append("=").append(map.get(key[i]));
        }
        basestring.append(secret);
        System.out.println("basestring:"+basestring.toString());
        System.out.println("MD5:"+ new Digester(DigestAlgorithm.MD5).digestHex(basestring.toString()));
        return new Digester(DigestAlgorithm.MD5).digestHex(basestring.toString());
    }

    /**
     * 通用请求验签类
     *
     * @param request
     * @throws Exception
     */
    public static void signVerify(RequestBase request) throws Exception {
        //开始验签
        if (!verify(request, apiKey, request.getSign())) {
            if ( !"intest".equals(request.getSign())) {
                throw new ServiceException(ServiceErrorCodeEnum.REQUEST_VERIFY_FAIL);
            }
        }
    }

    public static void signAndTokenVerify(RequestBase request) throws Exception {
        //开始验签
        if (!verify(request, apiKey, request.getSign())) {
            if ( !"intest".equals(request.getSign())) {
                throw new ServiceException(ServiceErrorCodeEnum.REQUEST_VERIFY_FAIL);
            }
        }
        if("intoken".equals(request.getToken())){
            return ;
        }
        TokenUtils.checkToken(request);
    }


    /**
     * 验签方法
     * @param bean Javabean对象
     * @param key 验签盐值
     * @param sign 签名密文
     * @return
     */
    public static boolean verify(Object bean, String key, String sign){
        boolean flag = false;
        try{
            Map paramsMap = beanToMap(bean);
            String orgStr = JSON.toJSONString(paramsMap).concat(key);
            log.info("验签原文："+orgStr);
            String genSign = new Digester(DigestAlgorithm.MD5).digestHex(orgStr.getBytes("UTF-8"));//指定编码格式
            log.info("验签密文："+genSign);
            if(genSign.equals(sign)){
                flag = true;
            }
        }catch (Exception e){
            log.info("验签失败",e);
        }
        return flag;
    }


    /**
     * 将JavaBean对象转化为TreeMap
     * @param bean
     * @return
     */
    public static TreeMap beanToMap(Object bean){
        TreeMap returnMap = new TreeMap();
        try {
            Class type = bean.getClass();
            BeanInfo beanInfo = Introspector.getBeanInfo(type);
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            //循环获取bean属性和值放入map中
            for (int i = 0; i < propertyDescriptors.length; i++) {
                PropertyDescriptor descriptor = propertyDescriptors[i];
                String propertyName = descriptor.getName();
                //忽略class和signValue属性
                if (!"class".equals(propertyName) && !"sign".equals(propertyName)) {
                    Method readMethod = descriptor.getReadMethod();
                    Object result = readMethod.invoke(bean, new Object[0]);
                    returnMap.put(propertyName, result);
                }
            }
        }catch (Exception e){
            log.info("beanToMap失败",e);
        }
        return returnMap;
    }


}
